//
//  KnowledgeBaseTest.cpp
//  IncrementalAlgorithm
//
//  Created by Roman Kutlak on 14/03/2011.
//  Copyright 2011 Roman Kutlak. All rights reserved.
//

#include <ctime>

#include <iostream>
#include <list>

#include "gtest/gtest.h"

#include "KnowledgeBase.h"
#include "Ontology.h"

using namespace std;
using namespace kutlak::roman::gre;

// Tests the default c'tor.
TEST(KnowledgeBase, DefaultConstructor) 
{
    // KnowledgeBase is an abstract class...test the derived classes
    KnowledgeBase kb;
    EXPECT_EQ(0, kb.Size());
}

TEST(KnowledgeBase, AltConstructor) 
{
    KnowledgeBase kb("/Users/roman/Projects/NLP/gre/resources/example.nt");
    KnowledgeBase kb2("bad/path");
    KnowledgeBase kb3;
    
    EXPECT_EQ(5, kb.Size());
    EXPECT_EQ(0, kb2.Size());
    EXPECT_EQ(0, kb3.Size());
    
    EXPECT_EQ(kb2, kb3);
    EXPECT_NE(kb, kb2);
    
    kb2 = kb;
    EXPECT_EQ(kb2, kb);
    kb3 = std::move(kb2);
    EXPECT_EQ(kb3, kb);
    EXPECT_NE(kb2, kb);
    
    KnowledgeBase::SetDefault
    (pKnowledgeBase
     (new KnowledgeBase("/Users/roman/Projects/NLP/gre/resources/example.nt")));

    EXPECT_EQ(kb, *KnowledgeBase::Default());
}

TEST(KnowledgeBase, Manipulation) 
{
    KnowledgeBase kb;
    EXPECT_EQ(0, kb.Size());
    
    kb.AddEntity(Entity());
    EXPECT_EQ(1, kb.Size());
    
    Entity e;
    EXPECT_EQ(e, *kb.EntityPtrById(-1));
    EXPECT_EQ(EntityPtrT(), kb.EntityPtrById(-5));
    
    EXPECT_EQ(e, *kb.EntityPtrByName("Entity"));
    EXPECT_EQ(EntityPtrT(), kb.EntityPtrByName("none"));
    
    kb.RemoveEntity(e);
    EXPECT_EQ(0, kb.Size());
    EXPECT_EQ(EntityPtrT(), kb.EntityPtrById(-1));
    EXPECT_EQ(EntityPtrT(), kb.EntityPtrByName("Entity"));
    
    
    kb.AddEntity(Entity("E"));
    EXPECT_EQ(1, kb.Size());
    
    std::vector<EntityPtrT> es = kb.Entities();
    EXPECT_EQ(1, es.size());
    
    Ontology::SetDefault
    (pOntology
     (new Ontology
      ("/Users/roman/Projects/NLP/gre/resources/dbpedia.nt")));
    
    
    kb.Clear();
    EXPECT_EQ(0, kb.Size());
    
    e.AddProperty(Ontology::kPersonType);
    e.AddProperty(Ontology::kThingType);
    e.SetName("JJ");
    Entity f("F");
    f.AddProperty(Ontology::kThingType);
    
    kb.AddEntity(Entity());
    kb.AddEntity(e);
    kb.AddEntity(f);
    EXPECT_EQ(3, kb.Size());
    
    kb.AddEntity(Entity());
    kb.AddEntity(Entity("JJ"));
    EXPECT_EQ(3, kb.Size());
    
    es = kb.EntitiesOfType(Ontology::kPersonType);
    EXPECT_EQ(1, es.size());
    
    es = kb.EntitiesOfType(Ontology::kThingType);
    EXPECT_EQ(2, es.size());
    
    EntityPtrT tmp = kb.EntityPtrByName("JJ");
    ASSERT_NE(EntityPtrT(), tmp);
    
    tmp->AddProperty(Property("attr", "cool val"));
    
    es = kb.Entities(Ontology::kPersonType);
    EXPECT_EQ(1, es.size());
    
    es = kb.Entities(Ontology::kThingType);
    EXPECT_EQ(2, es.size());
    
    es = kb.Entities(Property("attr", "cool val"));
    ASSERT_EQ(1, es.size());
    
    EXPECT_EQ("JJ", es.front()->Name());
    
    kb.Clear();
    EXPECT_EQ(0, kb.Size());
}

TEST(KnowledgeBase, AddFile) 
{
    KnowledgeBase kb;
    
    kb.AddFile("/Users/roman/Projects/NLP/gre/resources/example.nt");
    
    //    KnowledgeBase::const_iterator it;
    //    for (it = kb.begin(); it != kb.end(); ++it) 
    {
        //        cout << *it->second << endl;
        //    }
        EXPECT_EQ(5, kb.Size());
        
        KnowledgeBase kb2;
        
        kb2.AddFile("bad/path");
        EXPECT_EQ(0, kb2.Size());
        
    }
}

TEST(KnowledgeBase, AddFileFromStream) 
{
    KnowledgeBase kb2;
    
    stringstream input;
    input << "malformatted input";
    kb2.FromRdf(input);
    EXPECT_EQ(0, kb2.Size());
    kb2.Print();
    
    stringstream input2;
    input2 << "entity attribute value";
    //    cout << input2.str();
    kb2.FromRdf(input2);
    EXPECT_EQ(1, kb2.Size());
}

TEST(KnowledgeBase, EntityIterator) 
{
    Ontology::SetDefault(pOntology());
    KnowledgeBase kb("/Users/roman/Projects/NLP/gre/resources/example.nt");
    //    Voc& pf = Voc::Instance();
    size_t count = 0;
    EntityIterator it, end;
    for (it = kb.EntitiesBegin(), end = kb.EntitiesEnd();
         it != end; ++it) 
    {
        //        cout << it->Name() << " short type: " << it->Type() << " ";
        //        cout << "\ttype: ";
        //        cout << pf.AttributeStr(it->Type().Attribute());
        //        cout << " ";
        //        cout << pf.ValueStr(it->Type().Value());
        //        cout << endl;        
        ++count;
    }
    EXPECT_EQ(5, count);
    
    std::cout << "\n5 done." << std::endl;
    
    count = 0;
    for (it = kb.EntitiesBegin(Property(Ontology::kType, "desk")),
         end = kb.EntitiesEnd(); it != end; ++it)
    {
        ++count;
    }
    EXPECT_EQ(2, count);
}

TEST(KnowledgeBase, FileIO) 
{
    Ontology::SetDefault
    (pOntology
     (new Ontology
      ("/Users/roman/Projects/NLP/gre/resources/ontology.nt")));
    
    KnowledgeBase kb("/Users/roman/Projects/NLP/gre/resources/example.nt");
    EXPECT_EQ(5, kb.Size());

    EXPECT_EQ(kb, KnowledgeBase(kb));
    
    unlink("/tmp/kb.nt");
 
    kb.SaveFile("/tmp/kb.nt");
    KnowledgeBase kb2("/tmp/kb.nt");
    
    EXPECT_EQ(kb, kb2);
}

TEST(KnowledgeBase, Serialisation) 
{
    stringstream good, bad;
    KnowledgeBase kb("/Users/roman/Projects/NLP/gre/resources/example.nt");
    KnowledgeBase kb2(kb);
    KnowledgeBase kb3;
    
    good << kb;
    bad << "<knowledgebase> looks like kb but it is not a kb";
    
    EXPECT_EQ(kb, kb2);
    // this should throw
    try 
    {
        bad >> kb2;
    } 
    catch (DeserialisationException& de) 
    {
        cerr << de.what() << endl;
        cerr << "read: " << de.data() << endl;
    }
    // but kb2 should be unchanged
    EXPECT_EQ(kb, kb2);
    
    // this should throw
    try 
    {
        bad >> kb3;
    } 
    catch (DeserialisationException& de) 
    {
        cerr << de.what() << endl;
        cerr << "read: " << de.data() << endl;
    }
    EXPECT_NE(kb, kb3);
    
    // this should not throw
    try 
    {
        good >> kb3;
    } 
    catch (DeserialisationException& de) 
    {
        cerr << de.what() << endl;
        cerr << "read: " << de.data() << endl;
        EXPECT_EQ(true, false); // fail
    }
    EXPECT_EQ(kb, kb3);
    
    ofstream out("/tmp/kbtest.ser");
    out << kb;
    out.close();
    
    try {
        ifstream in("/tmp/kbtest.ser");
        in >> kb2;
    } catch (DeserialisationException& de) {
        EXPECT_EQ(true, false);
        cerr << de.what() << endl;
        cerr << "\t" << de.data() << endl;
    }
    
    EXPECT_EQ(kb, kb2);
}

